using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using CollabApp.Domain.Services.Rankings;
using CollabApp.Domain.Entities;
using CollabApp.Application.Interfaces;
using CollabApp.Domain.Repositories;
using CollabApp.Domain.Entities.Auth;
using System.Linq;

namespace CollabApp.Application.Services.Rankings;
    public class RankingService : IRankingService
    {
        private readonly IRedisService _redisService;
        private readonly IRepository<Ranking> _rankingRepo;
        private readonly IRepository<UserStatistics> _userStatsRepo;
        private const string OverallKey = "ranking:overall";
        private const string ProfilesKey = "ranking:profiles"; // Hash: field=userId, value=json {username, avatar}
        
        public RankingService(IRedisService redisService, IRepository<Ranking> rankingRepo, IRepository<UserStatistics> userStatsRepo)
        {
            _redisService = redisService;
            _rankingRepo = rankingRepo;
            _userStatsRepo = userStatsRepo;
        }

        public async Task<List<dynamic>> GetOverallRankingAsync(int page, int limit)
        {
            string key = OverallKey;
            long start = (page - 1) * limit;
            long stop = start + limit - 1;
            var results = await _redisService.SortedSetRangeByRankWithScoresAsync(key, start, stop, true);
            var enriched = new List<dynamic>();
            
            for (int i = 0; i < results.Count; i++)
            {
                var (member, score) = results[i];
                var profileJson = await _redisService.HashGetAsync(ProfilesKey, member);
                string username = string.Empty;
                string? avatarUrl = null;
                
                if (!string.IsNullOrWhiteSpace(profileJson))
                {
                    try
                    {
                        var profile = System.Text.Json.JsonSerializer.Deserialize<UserProfile>(profileJson);
                        if (profile != null)
                        {
                            username = profile.Username ?? string.Empty;
                            avatarUrl = profile.AvatarUrl;
                        }
                    }
                    catch { }
                }
                
                // 获取用户统计信息（胜率和场次）
                var userId = Guid.Parse(member);
                var userStats = await _userStatsRepo.GetSingleAsync(s => s.UserId == userId);
                var winRate = userStats?.WinRate ?? 0;
                var totalGames = userStats?.TotalGames ?? 0;
                
                enriched.Add(new { 
                    UserId = member, 
                    Username = username, 
                    AvatarUrl = avatarUrl, 
                    Score = score, 
                    Rank = start + i + 1,
                    WinRate = winRate,
                    TotalGames = totalGames
                });
            }
            
            return enriched;
        }

        public async Task<List<dynamic>> GetWeeklyRankingAsync(int page, int limit)
        {
            string key = $"ranking:weekly:{GetCurrentYearWeek()}";
            long start = (page - 1) * limit;
            long stop = start + limit - 1;
            var results = await _redisService.SortedSetRangeByRankWithScoresAsync(key, start, stop, true);
            var list = results.Select((x, i) => new {
                UserId = x.member,
                Score = x.score,
                Rank = start + i + 1
            }).Cast<dynamic>().ToList();
            return list;
        }

        public async Task<List<dynamic>> GetMonthlyRankingAsync(int page, int limit)
        {
            string key = $"ranking:monthly:{DateTime.UtcNow:yyyyMM}";
            long start = (page - 1) * limit;
            long stop = start + limit - 1;
            var results = await _redisService.SortedSetRangeByRankWithScoresAsync(key, start, stop, true);
            var list = results.Select((x, i) => new {
                UserId = x.member,
                Score = x.score,
                Rank = start + i + 1
            }).Cast<dynamic>().ToList();
            return list;
        }

        public async Task<List<dynamic>> GetFriendsRankingAsync(List<Guid> friendIds, int page, int limit)
        {
            string key = "ranking:overall";
            // 只查好友ID
            var all = new List<(string member, double score, long? rank)>();
            foreach (var id in friendIds)
            {
                var score = await _redisService.SortedSetScoreAsync(key, id.ToString());
                var rank = await _redisService.SortedSetRankAsync(key, id.ToString(), true);
                if (score.HasValue && rank.HasValue)
                    all.Add((id.ToString(), score.Value, rank.Value + 1));
            }
            // 按分数降序排序
            var sorted = all.OrderByDescending(x => x.score).ToList();
            var paged = sorted.Skip((page - 1) * limit).Take(limit)
                .Select(x => new { UserId = x.member, Score = x.score, Rank = x.rank })
                .Cast<dynamic>().ToList();
            return paged;
        }
        public Task<dynamic?> GetUserRankingAsync(Guid userId)
        {
            // TODO: PG 实现
            return Task.FromResult<dynamic?>(null);
        }
        public Task<dynamic?> GetRankingStatsAsync()
        {
            // TODO: PG 实现
            return Task.FromResult<dynamic?>(null);
        }
        public async Task<List<dynamic>> GetRankingByGameTypeAsync(string gameType, string period, int page, int limit)
        {
            await Task.CompletedTask;
            return new List<dynamic>();
        }
        public async Task<List<dynamic>> GetTierRankingAsync(string tier, int page, int limit)
        {
            await Task.CompletedTask;
            return new List<dynamic>();
        }

        // 定时同步Redis总榜到PGSQL
        public async Task SyncOverallRankingToPgsqlAsync(int topN = 1000)
        {
            string key = OverallKey;
            var top = await _redisService.SortedSetRangeByRankWithScoresAsync(key, 0, topN - 1, true);
            int rank = 1;
            var periodStart = DateTime.MinValue;
            var periodEnd = DateTime.MaxValue;
            foreach (var (userId, score) in top)
            {
                var parsedUserId = Guid.Parse(userId);
                var existing = await _rankingRepo.GetSingleAsync(r =>
                    r.UserId == parsedUserId &&
                    r.RankingType == CollabApp.Domain.Entities.RankingType.TotalScore &&
                    r.PeriodStart == periodStart &&
                    r.PeriodEnd == periodEnd
                );

                if (existing != null)
                {
                    existing.UpdateRanking(rank++, (int)score);
                    await _rankingRepo.UpdateAsync(existing);
                }
                else
                {
                    var entity = Ranking.CreateRanking(
                        parsedUserId,
                        CollabApp.Domain.Entities.RankingType.TotalScore,
                        rank++,
                        (int)score,
                        periodStart,
                        periodEnd
                    );
                    await _rankingRepo.AddAsync(entity);
                }
            }
            await _rankingRepo.SaveChangesAsync();
        }

        public async Task<(int Score, long Rank)> SubmitScoreAsync(Guid userId, string username, string? avatarUrl, int deltaScore)
        {
            if (userId == Guid.Empty) throw new ArgumentException("userId不能为空", nameof(userId));
            if (deltaScore == 0)
            {
                var existing = await _redisService.SortedSetScoreAsync(OverallKey, userId.ToString()) ?? 0;
                var existingRank = await _redisService.SortedSetRankAsync(OverallKey, userId.ToString(), true) ?? -1;
                return ((int)existing, existingRank >= 0 ? existingRank + 1 : 0);
            }

            // 更新分数（获取当前分数后累加，再写入）
            var userIdStr = userId.ToString();
            var currentScore = await _redisService.SortedSetScoreAsync(OverallKey, userIdStr) ?? 0;
            var newScore = currentScore + deltaScore;
            await _redisService.SortedSetAddAsync(OverallKey, userIdStr, newScore);

            // 写入/更新用户资料
            var profile = new UserProfile { Username = username, AvatarUrl = avatarUrl };
            var json = System.Text.Json.JsonSerializer.Serialize(profile);
            await _redisService.HashSetAsync(ProfilesKey, userIdStr, json);

            var rank = await _redisService.SortedSetRankAsync(OverallKey, userIdStr, true) ?? -1;
            return ((int)newScore, rank >= 0 ? rank + 1 : 0);
        }

        private class UserProfile
        {
            public string? Username { get; set; }
            public string? AvatarUrl { get; set; }
        }

        // 定时同步Redis周榜到PGSQL
        public async Task SyncWeeklyRankingToPgsqlAsync(int topN = 1000)
        {
            string weekKey = $"ranking:weekly:{GetCurrentYearWeek()}";
            var top = await _redisService.SortedSetRangeByRankWithScoresAsync(weekKey, 0, topN - 1, true);
            int rank = 1;
            var periodStart = GetCurrentWeekStart();
            var periodEnd = GetCurrentWeekEnd();
            foreach (var (userId, score) in top)
            {
                var entity = Ranking.CreateRanking(
                    Guid.Parse(userId),
                    CollabApp.Domain.Entities.RankingType.WeeklyScore,
                    rank++,
                    (int)score,
                    periodStart,
                    periodEnd
                );
                await _rankingRepo.AddAsync(entity);
            }
            await _rankingRepo.SaveChangesAsync();
        }

        // 定时同步Redis月榜到PGSQL
        public async Task SyncMonthlyRankingToPgsqlAsync(int topN = 1000)
        {
            string monthKey = $"ranking:monthly:{DateTime.UtcNow:yyyyMM}";
            var top = await _redisService.SortedSetRangeByRankWithScoresAsync(monthKey, 0, topN - 1, true);
            int rank = 1;
            var periodStart = GetCurrentMonthStart();
            var periodEnd = GetCurrentMonthEnd();
            foreach (var (userId, score) in top)
            {
                var entity = Ranking.CreateRanking(
                    Guid.Parse(userId),
                    CollabApp.Domain.Entities.RankingType.MonthlyScore,
                    rank++,
                    (int)score,
                    periodStart,
                    periodEnd
                );
                await _rankingRepo.AddAsync(entity);
            }
            await _rankingRepo.SaveChangesAsync();
        }

        // 工具方法：获取当前年份和周数
        private static string GetCurrentYearWeek()
        {
            var dt = DateTime.UtcNow;
            var cal = System.Globalization.CultureInfo.InvariantCulture.Calendar;
            int week = cal.GetWeekOfYear(dt, System.Globalization.CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
            return $"{dt:yyyy}_W{week:D2}";
        }
        private static DateTime GetCurrentWeekStart()
        {
            var dt = DateTime.UtcNow.Date;
            int diff = (7 + (dt.DayOfWeek - DayOfWeek.Monday)) % 7;
            return dt.AddDays(-1 * diff);
        }
        private static DateTime GetCurrentWeekEnd()
        {
            return GetCurrentWeekStart().AddDays(7);
        }
        private static DateTime GetCurrentMonthStart()
        {
            var dt = DateTime.UtcNow.Date;
            return new DateTime(dt.Year, dt.Month, 1);
        }
        private static DateTime GetCurrentMonthEnd()
        {
            var start = GetCurrentMonthStart();
            return start.AddMonths(1);
        }
    }